package cn.com.anysdk.ocr.service.impl;

import cn.com.anysdk.ocr.config.OcrProperties;
import cn.com.anysdk.ocr.config.OcrProperties.OcrKeyConfig;
import cn.com.anysdk.ocr.exception.OcrException;
import cn.com.anysdk.ocr.model.OcrOptions;
import cn.com.anysdk.ocr.model.OcrResult;
import cn.com.anysdk.ocr.service.IOcrService;
import com.aliyun.ocr_api20210707.Client;
import com.aliyun.ocr_api20210707.models.RecognizeBasicRequest;
import com.aliyun.ocr_api20210707.models.RecognizeBasicResponse;
import com.aliyun.ocr_api20210707.models.RecognizeBasicResponseBody;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import lombok.extern.slf4j.Slf4j;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 阿里云 OCR 服务实现
 */
@Slf4j
public class AliyunOcrService implements IOcrService {

    private final OcrProperties properties;
    private final Map<String, Client> clientCache = new ConcurrentHashMap<>();

    public AliyunOcrService(OcrProperties properties) {
        log.info("AliyunOcrService 初始化，配置：{}", properties);
        this.properties = validateProperties(properties);
    }

    /**
     * 验证配置属性
     */
    private OcrProperties validateProperties(OcrProperties properties) {
        if (properties == null) {
            throw new OcrException("OCR properties cannot be null");
        }
        if (properties.getAliyun() == null) {
            throw new OcrException("Aliyun OCR properties cannot be null");
        }
        return properties;
    }

    /**
     * 获取指定类型的OCR客户端
     */
    private Client getClient(String type) {
        return clientCache.computeIfAbsent(type, this::createClient);
    }

    /**
     * 创建阿里云 OCR 客户端
     */
    private Client createClient(String type) {
        try {
            OcrKeyConfig config = getOcrConfig(type);
            if (config == null) {
                throw new OcrException("No valid OCR configuration found for type: " + type);
            }

            Config clientConfig = new Config()
                    .setAccessKeyId(config.getAccessKeyId())
                    .setAccessKeySecret(config.getAccessKeySecret());
            
            String endpoint = config.getEndpoint();
            if (endpoint == null || endpoint.trim().isEmpty()) {
                endpoint = "ocr-api." + config.getRegion() + ".aliyuncs.com";
            }
            clientConfig.endpoint = endpoint;
            
            return new Client(clientConfig);
        } catch (Exception e) {
            log.error("Failed to create Aliyun OCR client for type: {}", type, e);
            throw new OcrException("Failed to create Aliyun OCR client for type: " + type, e);
        }
    }

    /**
     * 获取OCR配置
     */
    private OcrKeyConfig getOcrConfig(String type) {
        OcrKeyConfig config = null;
        switch (type.toLowerCase()) {
            case "general":
                config = properties.getAliyun().getGeneral();
                break;
            case "idcard":
                config = properties.getAliyun().getIdCard();
                break;
            case "bankcard":
                config = properties.getAliyun().getBankCard();
                break;
            case "businesslicense":
                config = properties.getAliyun().getBusinessLicense();
                break;
            default:
                config = properties.getAliyun().getGeneral();
        }
        
        if (config == null || 
            config.getAccessKeyId() == null || 
            config.getAccessKeyId().trim().isEmpty() ||
            config.getAccessKeySecret() == null || 
            config.getAccessKeySecret().trim().isEmpty()) {
            throw new OcrException("Invalid OCR configuration for type: " + type);
        }
        
        return config;
    }

    @Override
    public OcrResult recognize(File file, OcrOptions options) {
        try {
            log.debug("Recognizing image from file: {}", file.getAbsolutePath());
            byte[] imageBytes = java.nio.file.Files.readAllBytes(file.toPath());
            return recognize(imageBytes, options);
        } catch (IOException e) {
            log.error("Failed to read image file: {}", file.getAbsolutePath(), e);
            throw new OcrException("Failed to read image file: " + file.getAbsolutePath(), e);
        }
    }

    @Override
    public OcrResult recognize(InputStream inputStream, OcrOptions options) {
        try {
            log.debug("Recognizing image from input stream");
            byte[] imageBytes = inputStream.readAllBytes();
            return recognize(imageBytes, options);
        } catch (IOException e) {
            log.error("Failed to read image from input stream", e);
            throw new OcrException("Failed to read image from input stream", e);
        }
    }

    @Override
    public OcrResult recognize(byte[] imageBytes, OcrOptions options) {
        try {
            log.debug("Recognizing image from byte array, size: {} bytes", imageBytes.length);
            String type = options.getType() != null ? options.getType() : "general";
            Client client = getClient(type);
            
            RecognizeBasicRequest request = new RecognizeBasicRequest()
                    .setBody(new ByteArrayInputStream(imageBytes))
                    .setNeedRotate(options.isDetectOrientation());

            RecognizeBasicResponse response = client.recognizeBasicWithOptions(request, new RuntimeOptions());
            return convertToOcrResult(response.getBody());
        } catch (Exception e) {
            log.error("Failed to recognize image from byte array", e);
            throw new OcrException("Failed to recognize image from byte array", e);
        }
    }

    @Override
    public OcrResult recognize(String url, OcrOptions options) {
        try {
            log.debug("Recognizing image from URL: {}", url);
            String type = options.getType() != null ? options.getType() : "general";
            Client client = getClient(type);
            
            RecognizeBasicRequest request = new RecognizeBasicRequest()
                    .setUrl(url)
                    .setNeedRotate(options.isDetectOrientation());

            RecognizeBasicResponse response = client.recognizeBasicWithOptions(request, new RuntimeOptions());
            return convertToOcrResult(response.getBody());
        } catch (Exception e) {
            log.error("Failed to recognize image from URL: {}", url, e);
            throw new OcrException("Failed to recognize image from URL: " + url, e);
        }
    }

    /**
     * 将阿里云 OCR 响应转换为 OcrResult
     */
    private OcrResult convertToOcrResult(RecognizeBasicResponseBody body) {
        OcrResult result = new OcrResult();
        String dataStr = body.getData();
        if (dataStr != null && !dataStr.isEmpty()) {
            try {
                JSONObject dataJson = JSON.parseObject(dataStr);
                result.setText(dataJson.getString("content"));
                
                // 添加空值检查，并设置默认值
                Float confidence = dataJson.getFloat("prism_confidence");
                if (confidence != null) {
                    result.setConfidence(confidence);
                } else {
                    // 如果置信度为 null，设置一个默认值
                    result.setConfidence(1.0);
                }
                
                Float orientation = dataJson.getFloat("angle");
                if (orientation != null) {
                    result.setOrientation(orientation);
                }
                
                // prism_wordsInfo 是文本块信息
                if (dataJson.containsKey("prism_wordsInfo")) {
                    // TODO: 你可以根据 OcrResult.OcrBlock 的结构进一步完善
                    // 这里只是简单存储原始 JSON
                    result.setBlocks(null); // 或者你可以解析为 List<OcrResult.OcrBlock>
                }
            } catch (Exception e) {
                log.error("Failed to parse OCR response data: {}", dataStr, e);
                throw new OcrException("Failed to parse OCR response data", e);
            }
        }
        return result;
    }
}